/**  圆圈进度条  **/
/**  v-loading="boolean"  **/
/**  v-loading="{
      show: boolean
      text: string
      size: 'small' | 'medium' | 'large' | number
    }"  **/

import type { App, ObjectDirective } from 'vue'
import { createApp } from 'vue'
import { NSpin } from 'naive-ui'
import { uuid } from '@/utils/uuid'

type SizeType = 'small' | 'medium' | 'large' | number

type BindingValueType =
  | {
      show: boolean
      text: string
      size: SizeType
    }
  | boolean

const initLoading = (el, binding) => {
  const value: BindingValueType = binding.value
  let show,
    text = '正在加载中...',
    size: SizeType = 'medium'

  if (typeof value === 'boolean') {
    show = value
  } else {
    show = value.show
    text = value.text
    size = value.size
  }
  switch (show) {
    case true:
      setLoading(el, text, size)
      break
    case false:
      removeLoading(el)
      break
  }
}

export const loading: ObjectDirective = {
  mounted(el, binding) {
    initLoading(el, binding)
  },
  updated(el, binding) {
    initLoading(el, binding)
  },
  unmounted(el) {
    removeLoading(el)
  }
}

// spin实例Map列表
let $spinMap: Map<string, App<Element>> = new Map()

// 初始化+更新时
function setLoading(el: HTMLElement, text: string, size: SizeType) {
  // 如果已经有实例，则删除后重新初始
  if (getContainerId(el)) {
    removeLoading(el)
  }
  // 初始流程
  const parentEl = el
  const containerId = uuid()
  const spinContainer = document.createElement('div')
  spinContainer.className = 'spin-loading-container'
  spinContainer.id = containerId

  const elPosition = parentEl.getAttribute('position')
  if (elPosition == null || elPosition == 'static') {
    parentEl.style.position = 'relative'
  } else {
    parentEl.style.position = elPosition
  }

  const spinBody = document.createElement('div')
  spinContainer.appendChild(spinBody)
  spinBody.className = 'spin-loading-body'

  const $spin = createApp(NSpin, {
    show: true,
    description: text,
    size
  })
  $spin.mount(spinBody)
  $spinMap.set(containerId, $spin)

  parentEl.appendChild(spinContainer)
}

// 删除实例
function removeLoading(el: HTMLElement) {
  const containerId = getContainerId(el)
  if (containerId && $spinMap.has(containerId)) {
    $spinMap.get(containerId)?.unmount()
    $spinMap.delete(containerId)
    const loader = el.querySelector('.spin-loading-container')
    loader?.remove()
  }
}

// 获取实例id
function getContainerId(el: HTMLElement) {
  const loader = el.querySelector('.spin-loading-container')
  return loader ? loader.id : null
}
